package com.haircut.project.service.salary.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.haircut.project.constant.RecordConstant;
import com.haircut.project.dao.record.ProjectRecordMapper;
import com.haircut.project.dao.salary.UserSalaryMapper;
import com.haircut.project.model.record.ProjectRecord;
import com.haircut.project.model.salary.UserSalary;
import com.haircut.project.service.salary.ISalaryCalcService;
import com.haircut.project.service.salary.ISalaryService;
import com.haircut.project.vo.record.ApiRecordResVo;
import com.haircut.project.vo.salary.*;
import com.haircut.sys.dao.user.SysUserMapper;
import com.haircut.sys.model.user.SysUser;
import com.haircut.sys.service.permission.IPermissionService;
import com.haircut.sys.utils.PageUtils;
import com.haircut.sys.utils.user.SecurityUtils;
import com.modular.constant.BaseCodeDesc;
import com.modular.constant.NumberConstant;
import com.modular.exception.ModularException;
import com.modular.redis.RedisUtils;
import com.modular.result.PageResult;
import com.modular.utils.DBUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class SalaryServiceImpl implements ISalaryService {

    @Autowired
    private UserSalaryMapper userSalaryMapper;
    @Autowired
    private ProjectRecordMapper projectRecordMapper;
    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private IPermissionService permissionService;

    @Autowired
    @Qualifier("salaryExecutor")
    private AsyncTaskExecutor asyncTaskExecutor;

    @Autowired
    private ISalaryCalcService salaryCalcService;
    @Autowired
    private RedisUtils redisUtils;




    /**
     * 查询管理分页
     * @param reqVo
     * @return
     */
    @Override
    public PageResult<SalaryResVo> findManagerPage(SalaryReqVo reqVo)
    {
        reqVo.setDataScopeList(permissionService.findPermissionSqlList("t.user_id","t.dept_id"));
        reqVo.setUserName(DBUtils.escapeQuryLikeParam(reqVo.getUserName()));
        IPage<?> pageReq = Page.of(reqVo.getPageNum(),reqVo.getPageSize());
        Page<SalaryResVo> page = userSalaryMapper.findManagerPage(pageReq,reqVo);
        if (ObjectUtil.isNull(page) || CollUtil.isEmpty(page.getRecords())) {
            return PageUtils.cover(reqVo);
        }
        List<String> userIdList =page.getRecords().stream().map(SalaryResVo::getUserId).distinct().toList();
        List<SysUser> userList = sysUserMapper.selectBatchIds(userIdList);
        Map<String,SysUser> sysUserMap = userList.stream().collect(Collectors.toMap(SysUser::getId,p->p));
        SysUser sysUser = null;
        for(SalaryResVo resVo:page.getRecords()){
            sysUser = sysUserMap.get(resVo.getUserId());
            resVo.setUserName(sysUser.getRealName());
        }


        return PageUtils.cover(page);
    }

    /**
     * 发工资
     * @param salaryId
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void salarySend(String salaryId)
    {
        UserSalary userSalary = userSalaryMapper.selectById(salaryId);
        if(ObjectUtil.isNull(userSalary)){
            throw new ModularException(BaseCodeDesc.DEFATUL_MSG, "工资信息错误");
        }
        if(ObjectUtil.equal(userSalary.getSalaryType(), RecordConstant.SalaryType.ERR_INIT)){
            throw new ModularException(BaseCodeDesc.DEFATUL_MSG, "有未核算的记录，无法继续发工资");
        }
        if(ObjectUtil.notEqual(userSalary.getSalaryType(),RecordConstant.SalaryType.NEW_RECORD)){
            throw new ModularException(BaseCodeDesc.DEFATUL_MSG, "工资已发放，无法继续发工资");
        }
        String userId = SecurityUtils.getUserId();
        Date currDate = DateUtil.date();
        LambdaUpdateWrapper<UserSalary> salaryModify = Wrappers.lambdaUpdate();
        salaryModify.set(UserSalary::getPayUserId,userId).set(UserSalary::getPayDate,currDate);
        salaryModify.set(UserSalary::getModifyUserId,userId).set(UserSalary::getModifyDate,currDate);
        salaryModify.set(UserSalary::getSalaryType, RecordConstant.SalaryType.SEND_SALARY);
        salaryModify.eq(UserSalary::getId,salaryId);
        userSalaryMapper.update(salaryModify);

        LambdaUpdateWrapper<ProjectRecord> recordModify = Wrappers.lambdaUpdate();
        recordModify.set(ProjectRecord::getPayUserId,userId).set(ProjectRecord::getPayDate,currDate);
        recordModify.set(ProjectRecord::getModifyUserId,userId).set(ProjectRecord::getModifyDate,currDate);
        recordModify.set(ProjectRecord::getRecordType, RecordConstant.RecordType.PAY_RECORD);
        recordModify.eq(ProjectRecord::getFinanceId,salaryId);
        projectRecordMapper.update(recordModify);

    }
    /**
     * 重新计算
     * @param salaryId
     */
    @Override
    public void recalculation(String salaryId)
    {
        UserSalary salary = userSalaryMapper.selectById(salaryId);
        if(ObjectUtil.isNull(salary)){
            throw new ModularException(BaseCodeDesc.DEFATUL_MSG,"工资错误");
        }
        String redisKey = StrUtil.format("recalculation:{}",salaryId);
        if(redisUtils.checkRedisKey(redisKey)){
            throw new ModularException(BaseCodeDesc.DEFATUL_MSG,"正在计算，请稍后");
        }

        asyncTaskExecutor.execute(()->{
            try {
                salaryCalcService.recalculation(salary, redisKey);
            }finally {
                redisUtils.delKey(redisKey);
            }
        });

    }

    @Override
    public PageResult<ApiSalaryResVo> findApiPage(ApiSalaryReqVo reqVo)
    {

        IPage<?> pageReq = Page.of(reqVo.getPageNum(),reqVo.getPageSize());
        Page<ApiSalaryResVo> page = userSalaryMapper.findApiPage(pageReq,reqVo);
        if (ObjectUtil.isNull(page) || CollUtil.isEmpty(page.getRecords())) {
            return PageUtils.cover(reqVo);
        }

        for(ApiSalaryResVo resVo:page.getRecords()){
            resVo.calc();
        }
        return PageUtils.cover(page);
    }

    /**
     * 确认工资
     * @param reqVo
     */
    @Override
    public void confirmSalary(ApiSalaryConfirmVo reqVo)
    {
        UserSalary salary = userSalaryMapper.selectById(reqVo.getId());
        if(ObjectUtil.isNull(salary)){
            throw new ModularException(BaseCodeDesc.DEFATUL_MSG,"工资错误");
        }
        if(ObjectUtil.equal(salary.getSalaryNo(), RecordConstant.SalaryType.SEND_SALARY)){
            throw new ModularException(BaseCodeDesc.DEFATUL_MSG,"没有到确认工资环节，不能操作");
        }
        if(ObjectUtil.equal(salary.getSalaryNo(), RecordConstant.SalaryType.CONFIRM_SALARY)){
            throw new ModularException(BaseCodeDesc.DEFATUL_MSG,"已经确认完成，不能操作");
        }
        if(ObjectUtil.notEqual(salary.getUserId(), SecurityUtils.getUserId())){
            throw new ModularException(BaseCodeDesc.DEFATUL_MSG,"不是自己的工资，不能操作");
        }
        LambdaUpdateWrapper<UserSalary> modify = Wrappers.lambdaUpdate();
        modify.set(UserSalary::getSalaryType, RecordConstant.SalaryType.CONFIRM_SALARY);
        modify.set(UserSalary::getModifyUserId,SecurityUtils.getUserId()).set(UserSalary::getModifyDate,DateUtil.date());
        modify.set(UserSalary::getConfirmRemark,reqVo.getConfirmRemark());
        modify.set(UserSalary::getConfirmDate,DateUtil.date());
        modify.eq(UserSalary::getId,salary.getId());
        userSalaryMapper.update(modify);
    }

}
